package org.bookie.framework.components;

import static org.bookie.core.services.impl.Listener.Event.USER_LOGGED_IN;

import javax.mail.MessagingException;

import org.apache.tapestry.IActionListener;
import org.apache.tapestry.IComponent;
import org.apache.tapestry.IRequestCycle;
import org.apache.tapestry.annotations.Persist;
import org.apache.tapestry.form.Form;
import org.apache.tapestry.form.IFormComponent;
import org.apache.tapestry.valid.ValidationConstants;
import org.bookie.core.services.IUserManager;
import org.bookie.core.services.IAuthenticationContext.AuthenticationResult;
import org.bookie.core.services.IUserManager.RegistrationResult;
import org.bookie.framework.CommonBaseComponent;
import org.bookie.model.Role;
import org.bookie.model.User;

/**
 * Login component encapsulates login/register functionalitites
 * 
 * @author zubairov
 *
 */
public abstract class LoginComponent extends CommonBaseComponent {

	public static final String DIALOG_COMPONENT_ID = "loginDialog";

	public abstract void setRegMode(boolean value);
	
	public abstract void setEmail(String string);
	
	public abstract void setUsername(String userName);
	
	/**
	 * Injected service
	 * @return
	 */
	public abstract IUserManager getUserManager();
	
	/**
	 * Parameter setter 
	 */
	public abstract void setVisible(boolean value);
	
	/**
	 * A name of the component that trigger login
	 * @return
	 */
	@Persist
	public abstract String getTriggerComponent();
	
	/**
	 * Setter for the triggerComponent
	 * @param name
	 */
	public abstract void setTriggerComponent(String name);
	
	/**
	 * Login user
	 * @param email
	 * @param password
	 * @param cycle
	 */
	public void login(String email, String password, Boolean rememberMe) {
		updateMe();
		if (getDelegate().getHasErrors()) return;
		AuthenticationResult result = getAuthenticationContext().authenticate(email, password, rememberMe);
		if (AuthenticationResult.USER_NOT_FOUND.equals(result)
				|| AuthenticationResult.EMAIL_PASSWORD_DISCREPANCE.equals(result)) {
			recordError("form.failure");
			return;
		}
		if (AuthenticationResult.USER_NOT_ACTIVE.equals(result)){
			formatError("form.user.inactive", email);
			return;
		}
		notifyLoginListeners();
	}
	
	/**
	 * Switch form to the registration view
	 * @param cycle
	 */
	public void switchRegister() {
		setRegMode(true);
		updateMe();
		getDelegate().setFormComponent((IFormComponent) getComponent("email"));
		String emailValue = getDelegate().getFieldInputValue();
		getDelegate().clearErrors();		
		Form form = (Form) getComponent("loginForm");		
		if (emailValue == null || emailValue.length() == 0) {
			form.registerForFocus((IFormComponent) getComponent("email"), ValidationConstants.ERROR_FIELD);
			// Do nothing
			return;
		}
		setEmail(emailValue);
		setUsername(emailValue);
		form.registerForFocus((IFormComponent) getComponent("confirm"), ValidationConstants.ERROR_FIELD);
	}
	
	/**
	 * Register a new user
	 * @param email
	 * @param password
	 * @param confirm
	 * @param username
	 * @throws MessagingException 
	 */
	public void register(String email, String password, String confirm, String username) throws MessagingException {
		updateMe();
		if (getDelegate().getHasErrors()) return;
		if (!password.equals(confirm)) {
			recordError("form.password.consistency");
			return;
		}
		User user = new User(username, password, email);
		//TODO Temporarily fix until our mails aren't spamed anymore.
		user.setRole(Role.USER);
		RegistrationResult result = getUserManager().registerNewUser(user);
		if (result == RegistrationResult.DUPLICATE_EMAIL) {
			recordError("form.email.exists");
			return;
		}
		if (result == RegistrationResult.DUPLICATE_USERNAME) {
			recordError("form.username.exists");
			return;
		}
		setRegMode(false);
		setMessage(getMessages().getMessage("register.success.message"));
	}


	/**
	 * Notify listeners that user have logged in
	 */
	private void notifyLoginListeners() {
		getNotificationService().notifyListeners(USER_LOGGED_IN, String.valueOf(getTriggerComponent()));
		setTriggerComponent(null);
	}

	
	/**
	 * Updates itself
	 * @param cycle
	 */
	private void updateMe() {
		getResponseBuilder().updateComponent(DIALOG_COMPONENT_ID);
	}
	
	/**
	 * Hides message
	 */
	public void hideMessage() {
		setMessage(null);
		updateMe();
		notifyLoginListeners();
	}

	/**
	 * Cancel registration
	 * @param cycle
	 */
	public void cancelRegister() {
		setRegMode(false);
		getDelegate().clear();
		updateMe();
		setTriggerComponent(null);
	}
	
	/**
	 * Returns a listener that switches the login component
	 * into registration mode
	 * 
	 * @return
	 */
	public IActionListener getRegisterListener() {
		return new IActionListener() {
		
			public void actionTriggered(IComponent component, IRequestCycle cycle) {
				setRegMode(true);
				updateMe();
				setTriggerComponent(component.getClientId());
			}

			public String getMethodName() {
				return "unknown";		
			}

		};
	}
	
	/**
	 * Returned listener trigger login dialog to be shown
	 * 
	 * @return
	 */
	public IActionListener getLoginListener() {
		return new IActionListener() {
		
			public void actionTriggered(IComponent component, IRequestCycle cycle) {
				setVisible(true);
				updateMe();
				setTriggerComponent(component.getClientId());
			}

			public String getMethodName() {
				return "unknown";		
			}
		
		};
	}
}
